/* eslint-disable no-unused-vars */
import namespace from "./namespace";

import $ from "jquery";
import uuid from "shortid";
// import Snap from "snapsvg";
import Raphael from "raphael";
import EventEmitter from "eventemitter3";

import ArrayList from "./util/ArrayList";
import Selection from "./util/Selection";

/**
 * 画布类
 */
class Canvas extends EventEmitter {
  static EVENTS = {
    FIGURE_ADD: "figure:add",
    FIGURE_REMOVE: "figure:remove",
    FIGURE_SELECTION: "figure:selection", // 单图框选中
    FIGURE_UNSELECTION: "figure:unselection" // 单图取消框选
  };

  NAME = `${namespace.namespace}.Canvas`;

  constructor() {
    super();
    // 基础绘制图形
    this.figures = new ArrayList();

    // 框选中的图形
    this.selection = new Selection();

    this.policys = new ArrayList();

    // 交互类图形,比如放大缩小,旋转...
    this.handleFigures = new ArrayList();

    // 交互策略
    this.installPolicy(new namespace.policy.canvas.SelectionPolicy());
    this.installPolicy(new namespace.policy.canvas.DefaultKeyboardPolicy());

    // redo/undo
    this.commandStack = new namespace.CommandStack();

    this.init.apply(this, arguments);
  }

  init(dom, width, height) {
    this.canvasId = uuid();
    this.$html = $(dom);
    this.dom = this.$html[0];
    this.$html.css({
      cursor: "default",
      "-webkit-tap-highlight-color": "rgba(0,0,0,0)"
    });

    if (!isNaN(parseFloat(width)) && !isNaN(parseFloat(height))) {
      this.initialWidth = width;
      this.initialHeight = height;
    } else {
      this.initialWidth = this.getWidth();
      this.initialHeight = this.getHeight();
    }

    if (!isNaN(parseFloat(height))) {
      this.canvas = new Raphael(this.dom, width, height);
    } else {
      this.canvas = new Raphael(this.dom, this.getWidth(), this.getHeight());
    }

    this.canvas.paper = this.canvas;

    this.paper = this.canvas;

    this.Raphael = Raphael;
    // this.canvas.appendTo(this.$html[0]);

    this.initEvent();
  }

  initEvent() {
    // DragDrop status handling
    this.mouseDown = false;
    this.mouseDownX = 0;
    this.mouseDownY = 0;
    this.mouseDragDiffX = 0;
    this.mouseDragDiffY = 0;

    // 鼠标按下
    this.$html.bind("mousedown", event => {
      event.preventDefault();

      let pos = this.fromDocumentToCanvasCoordinate(
        event.clientX,
        event.clientY
      );

      this.mouseDown = true;
      this.mouseDownX = event.clientX;
      this.mouseDownY = event.clientY;
      this.mouseDragDiffX = 0;
      this.mouseDragDiffY = 0;

      // 单一行为过多之后,需要取最新值
      let uniquePolicy = this.policys.find(
        policy => policy instanceof namespace.policy.canvas.UniquePolicy
      );

      // 单一行为
      if (uniquePolicy) {
        uniquePolicy.onMouseDown(this, pos.x, pos.y);
        return;
      }

      // 多行为
      this.policys.each((i, policy) => policy.onMouseDown(this, pos.x, pos.y));
    });

    // 鼠标移动
    this.$html.bind("mousemove", event => {
      let pos = this.fromDocumentToCanvasCoordinate(
        event.clientX,
        event.clientY
      );

      let diffXAbs = event.clientX - this.mouseDownX;
      let diffYAbs = event.clientY - this.mouseDownY;

      // 未按下鼠标
      if (!this.mouseDown) {
        this.policys.each((i, policy) => {
          policy.onMouseMove(this, pos.x, pos.y);
        });
        return;
      }

      // 已按下鼠标
      this.policys.each((i, policy) => {
        policy.onMouseDrag(
          this,
          diffXAbs,
          diffYAbs,
          diffXAbs - this.mouseDragDiffX,
          diffYAbs - this.mouseDragDiffY,
          pos
        );
      });

      this.mouseDragDiffX = diffXAbs;
      this.mouseDragDiffY = diffYAbs;
    });

    // 鼠标抬起
    this.$html.bind("mouseup", event => {
      if (this.mouseDown === false) {
        return;
      }

      this.mouseDown = false;
      let pos = this.fromDocumentToCanvasCoordinate(
        event.clientX,
        event.clientY
      );

      let uniquePolicy = this.policys.find(policy => policy.isPrimary);

      // 单一行为
      if (uniquePolicy) {
        uniquePolicy.onMouseUp(this, pos.x, pos.y);
        return;
      }

      this.policys.each((i, policy) => {
        policy.onMouseUp(this, pos.x, pos.y);
      });

      this.mouseDragDiffX = 0;
      this.mouseDragDiffY = 0;
    });

    // 键盘按下/抬起
    $(document).bind("keyup keydown", event => {
      this.policys.each((i, policy) => {
        if (policy instanceof namespace.policy.canvas.KeyboardPolicy) {
          policy[event.type === "keyup" ? "onKeyUp" : "onKeyDown"](
            this,
            event.keyCode,
            event
          );
        }
      });
    });
  }

  getBestFigure(x, y, blacklist, whitelist) {
    // handleFigures;
    for (let i = 0, len = this.handleFigures.getSize(); i < len; i++) {
      let tempResult = this.handleFigures.get(i);
      if (tempResult.hitTest(x, y)) {
        return tempResult;
      }
    }

    let result = null;
    // base figure
    for (let i = this.figures.getSize() - 1; i >= 0; i--) {
      let figure = this.figures.get(i);
      if (result === null && figure.hitTest(x, y)) {
        result = figure;
        return result;
      }

      if (result !== null) {
        return result;
      }
    }

    return result;
  }

  // 画板宽
  getWidth() {
    return this.$html.width();
  }

  // 画板高
  getHeight() {
    return this.$html.height();
  }

  fromDocumentToCanvasCoordinate(x, y) {
    return new namespace.geo.Point(
      x - this.getAbsoluteX() - parseFloat(this.$html.css("padding-left")),
      y - this.getAbsoluteY() - parseFloat(this.$html.css("padding-top"))
    );
  }

  getAbsoluteX() {
    return this.$html.offset().left;
  }

  getAbsoluteY() {
    return this.$html.offset().top;
  }

  /**
   * 添加图形
   * @param {*} figure
   */
  addFigure(figure) {
    if (figure.getCanvas() === this) {
      return;
    }

    figure.setCanvas(this);

    figure.getShapeElement();

    figure.repaint();

    figure.installPolicy(
      new namespace.policy.figure.RegionPolicy(
        0,
        0,
        this.getWidth(),
        this.getHeight()
      )
    );

    this.figures.add(figure);

    this.emit(Canvas.EVENTS.FIGURE_ADD, { figure, canvas: this });
  }

  /**
   * 删除图形
   * @param {} figure
   */
  removeFigure(figure) {
    if (this.getSelection().contains(figure)) {
      this.policys.each((i, policy) => {
        if (typeof policy.unselect === "function") {
          policy.unselect(figure);
        }
      });
    }

    figure.setCanvas(null);
    this.figures.remove(figure);
  }

  getFigures() {
    return this.figures;
  }

  installPolicy(policy) {
    const _constructor = namespace.policy.canvas.UniquePolicy;

    // 确保仅有一个唯一策略
    if (policy instanceof _constructor) {
      this.policys.filter(p => {
        let isUniquePolicyPolicy = p instanceof _constructor;
        isUniquePolicyPolicy && p.onUninstall(this);
        return !isUniquePolicyPolicy;
      });
    }

    policy.onInstall(this);

    this.policys.add(policy);
  }

  uninstallPolicy(policy) {
    if (policy === null) {
      return; //silently
    }

    let removed = this.policys.remove(policy);
    if (removed !== null) {
      removed.onUninstall(this);
    }
    // 也可以通过NAME来移除
    return this;
  }

  // 框选中的图形
  getSelection() {
    return this.selection;
  }
}

namespace.Canvas = Canvas;
